package com.amap.maps.jsmap.demo.smoothmove;

import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.model.BasePointOverlay;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.maps.model.LatLng;

import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 按照指定的经纬度数据和时间，平滑移动
 */
public class MovingPointOverlay {

    private AMap mAMap;
    /**
     * 默认总时长为10000ms
     **/
    private long duration = 10000L;
    private long mStepDuration = 20L;

    /**
     * 每段点的队列，第一个点为起点
     **/
    private LinkedList<LatLng> points = new LinkedList<LatLng>();
    /**
     * 每段距离队列  大小为points.size() - 1
     **/
    private LinkedList<Double> eachDistance = new LinkedList<Double>();
    private double totalDistance = 0;
    private double remainDistance = 0;
    private ExecutorService mThreadPools;
    private Object mLock = new Object();

    /**
     * Marker位置
     **/
    private BasePointOverlay baseOverlay = null;


    /**
     * 当前动画在第几段，points中上一个点的下标
     **/
    private int index = 0;

    private boolean useDefaultDescriptor = false;

    AtomicBoolean exitFlag = new AtomicBoolean(false);

    private MoveListener moveListener;

    private enum MOVESTATUS {
        /**
         *
         */
        ACTION_UNKNOWN,
        /**
         *
         */
        ACTION_START,
        /**
         *
         */
        ACTION_RUNNING,
        /**
         *
         */
        ACTION_PAUSE,
        /**
         *
         */
        ACTION_STOP
    }

    /**
     * 记录状态
     */
    private MOVESTATUS STATUS = MOVESTATUS.ACTION_UNKNOWN;

    /**
     * 用于记录暂停时间，恢复动画时，将暂停的时间添加到mAnimationBeginTime中去，防止动画暂停后跳跃
     */
    private long pauseMillis;
    /**
     * 动画起始时间
     */
    private long mAnimationBeginTime = System.currentTimeMillis();

    /**
     * 平滑移动时返回剩余距离接口
     *
     * @since V6.3.1
     */
    public interface MoveListener {
        /**
         * 返回 距离终点的距离
         *
         * @param distance 单位 米
         * @since V6.3.1
         */
        void move(double distance);
    }

    private class MoveThread implements ThreadFactory {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r, "MoveSmoothThread");
            return thread;
        }
    }

    /**
     * 根据给定的参数来构造SmoothMoveMarker对象。
     *
     * @param mAMap 地图AMap对象
     * @since V6.3.1
     */
    public MovingPointOverlay(AMap mAMap, BasePointOverlay overlay) {
        if (mAMap == null || overlay == null) {
            return;
        }
        this.mAMap = mAMap;
        mThreadPools = new ThreadPoolExecutor(1, 2, 5, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new MoveThread());
        baseOverlay = overlay;
    }


    /**
     * 设置平滑移动的经纬度数组
     *
     * @param points 平滑移动轨迹的坐标数组
     * @since V6.3.1
     */
    public void setPoints(List<LatLng> points) {
        synchronized (mLock) {
            try {
                /**
                 * 传入的点必须大于或等于2
                 */
                if (points == null || points.size() < 2) {
                    return;
                }
                stopMove();
                this.points.clear();
                for (LatLng latLng : points) {
                    if (latLng != null) {
                        this.points.add(latLng);
                    }
                }

                eachDistance.clear();
                totalDistance = 0;

                //计算比例
                for (int i = 0; i < this.points.size() - 1; i++) {
                    double distance = AMapUtils.calculateLineDistance(this.points.get(i), this.points.get(i + 1));
                    eachDistance.add(distance);
                    totalDistance += distance;
                }
                remainDistance = totalDistance;

                LatLng latLng = this.points.get(0);
                baseOverlay.setPosition(latLng);
                reset();
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 点更新后需要恢复成初始状态。
     */
    private void reset() {
        try {
            if (STATUS == MOVESTATUS.ACTION_RUNNING || STATUS == MOVESTATUS.ACTION_PAUSE) {
                exitFlag.set(true);
                mThreadPools.awaitTermination(mStepDuration + 20, TimeUnit.MILLISECONDS);
//                baseOverlay.setAnimation(null);
                STATUS = MOVESTATUS.ACTION_UNKNOWN;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void resetIndex() {
        index = 0;
    }

    /**
     * 设置平滑移动的总时间
     *
     * @param duration 单位: 秒
     * @since V6.3.1
     */
    public void setTotalDuration(int duration) {
        this.duration = duration * 1000;
    }

    /**
     * 开始平滑移动
     *
     * @since V6.3.1
     */
    public void startSmoothMove() {
        if (STATUS == MOVESTATUS.ACTION_PAUSE) {
            STATUS = MOVESTATUS.ACTION_RUNNING;
            long grapTime = System.currentTimeMillis() - pauseMillis;
            mAnimationBeginTime += grapTime;
            return;
        }
        if (STATUS == MOVESTATUS.ACTION_UNKNOWN || STATUS == MOVESTATUS.ACTION_STOP) {
            if (points.size() < 1) {
                return;
            }
            index = 0;
            try {
                mThreadPools.execute(new SmoothMoveRunnable());
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }


    private LatLng getCurPosition(long offset) {
        if (offset > duration) {
            exitFlag.set(true);
//            IPoint curGeoPoint = new IPoint();
            index = points.size() - 1;
            LatLng curPos = points.get(index);
            index--;
            index = Math.max(index, 0);
            remainDistance = 0;
//            MapProjection.lonlat2Geo(curPos.longitude, curPos.latitude, curGeoPoint);
            if (moveListener != null) {
                moveListener.move(remainDistance);
            }
            return curPos;

        } else {
            double curDis = offset * totalDistance / duration;
            remainDistance = totalDistance - curDis;
            int curIndex = 0;
            double dot = 1;
            for (int i = 0; i < eachDistance.size(); i++) {
                double tmpDis = eachDistance.get(i);
                if (curDis > tmpDis) {
                    curDis -= tmpDis;
                } else {
                    if (tmpDis > 0) {
                        dot = curDis / tmpDis;
                    }
                    curIndex = i;
                    break;
                }
            }

            if (curIndex != index) {
                if (moveListener != null) {
                    moveListener.move(remainDistance);
                }
            }

            index = curIndex;
            LatLng curPos = points.get(curIndex);
            LatLng nextPos = points.get(curIndex + 1);

            double offsetX = nextPos.latitude - curPos.latitude;
            double offsetY = nextPos.longitude - curPos.longitude;

            float distance = AMapUtils.calculateLineDistance(curPos, nextPos);
            /**角度变化太小的情况可以不处理，防止抖动**/
            if (distance > 1f) {
                float rotate = getRotate(curPos, nextPos);
                if(mAMap != null) {
                    CameraPosition cameraPosition = mAMap.getCameraPosition();
                    if(cameraPosition != null) {
                        baseOverlay.setRotateAngle(360 - rotate + cameraPosition.bearing);
                    }
                }

            }

            return new LatLng((curPos.latitude + offsetX * dot), (curPos.longitude + offsetY * dot));
        }

    }

    /**
     * 根据经纬度计算需要偏转的角度
     *
     * @param curPos
     * @param nextPos
     * @return
     */
    private float getRotate(LatLng curPos, LatLng nextPos) {
        if (curPos == null || nextPos == null) {
            return 0;
        }
        double x1 = curPos.latitude;
        double x2 = nextPos.latitude;
        double y1 = curPos.longitude;
        double y2 = nextPos.longitude;

        float rotate = (float) (Math.atan2(y2 - y1, x2 - x1) / Math.PI * 180);
        return rotate;
    }

    /**
     * 停止平滑移动
     *
     * @since V6.3.1
     */
    public void stopMove() {
        if (STATUS == MOVESTATUS.ACTION_RUNNING) {
            STATUS = MOVESTATUS.ACTION_PAUSE;
            pauseMillis = System.currentTimeMillis();
        }
    }

    /**
     * 获取当前移动的Marker
     *
     * @return 放回当期那移动的Marker
     * @since V6.3.1
     */
    public BasePointOverlay getObject() {
        return baseOverlay;
    }

    /**
     * 获取当前位置坐标
     *
     * @return 返回当前Marker的坐标
     * @since V6.3.1
     */
    public LatLng getPosition() {
        if (baseOverlay != null) {
            return baseOverlay.getPosition();
        }

        return null;
    }

    /**
     * 获取某段索引
     *
     * @return 返回某段索引
     * @exclude javadoc中不显示 用户暂时不需要
     */
    public int getIndex() {
        return index;
    }


    /**
     * 停止滑动并销毁Marker
     *
     * @since V6.3.1
     */
    public void destroy() {
        try {
            removeMarker();
            mThreadPools.shutdownNow();
            synchronized (mLock) {
                points.clear();
                eachDistance.clear();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * @exclude javadoc中不显示
     */
    public void removeMarker() {
        try {
            reset();
            if (baseOverlay != null) {
                baseOverlay.remove();
                baseOverlay = null;
            }
            points.clear();
            eachDistance.clear();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * @exclude javadoc中不显示
     */
    public void setPosition(LatLng position) {
        try {
            if (baseOverlay != null) {
                baseOverlay.setPosition(position);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置Marker 角度
     *
     * @exclude javadoc中不显示 咱不提供给用户，设置角度有问题
     */
    public void setRotate(float rotate) {
        try {

            if (baseOverlay != null) {
                if(mAMap != null) {
                    CameraPosition cameraPosition = mAMap.getCameraPosition();
                    if(cameraPosition != null) {
                        baseOverlay.setRotateAngle(360 - rotate + cameraPosition.bearing);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置 Marker 是否可见
     *
     * @param b true 可见  false  不可见
     * @since V6.3.1
     */
    public void setVisible(boolean b) {
        try {
            if (baseOverlay != null) {
                baseOverlay.setVisible(b);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 设置Marker移动的回调方法
     *
     * @param moveListener 回调监听参数
     * @since V6.3.1
     */
    public void setMoveListener(MoveListener moveListener) {
        this.moveListener = moveListener;
    }


    private class SmoothMoveRunnable implements Runnable {
        @Override
        public void run() {
            try {
                mAnimationBeginTime = System.currentTimeMillis();
                STATUS = MOVESTATUS.ACTION_START;
                exitFlag.set(false);
                while (!exitFlag.get() && index <= points.size() - 1) {

                    synchronized (mLock) {
                        if (exitFlag.get()) {
                            return;
                        }
                        if (STATUS != MOVESTATUS.ACTION_PAUSE) {
                            long offset = System.currentTimeMillis() - mAnimationBeginTime;
                            LatLng curPos = getCurPosition(offset);
                            baseOverlay.setPosition(curPos);
                            STATUS = MOVESTATUS.ACTION_RUNNING;
                        }
                    }
                    Thread.sleep(mStepDuration);
                }
                STATUS = MOVESTATUS.ACTION_STOP;
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
    }

}
